/*
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    2013 year
 *    Author: Esipenko Aleksey (Alexorleon)
 *    E-Mail: alexorleon@gmail.com
 */

#include "qrgen.h"
#include <QPainter>
#include <QPixmap>
#include <QDateTime>
#include <QDebug>
//#include <QRegExp>

namespace ConstInFunctions {

    const int legthTableAlphanumeric = 45;
    const int characters = 41;
    const int bits = 41;
}

QRgen::QRgen() : typeData(-1), lengthMessage(0), version(0),
    binaryMessage(""), message(""), tmpStr(""), finishedBinaryMessage("") {

}

// GetAlphanumeric возвращает индекс символа из массива
int QRgen::GetAlphanumeric(const QChar &_chr) {

    const QChar tableAlphanumeric[ConstInFunctions::legthTableAlphanumeric] = {

        '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
        'P','Q','R','S','T','U','V','W','X','Y','Z',' ','$','%','*','+','-','.','/',':'
    };

    for (int i = 0; i < ConstInFunctions::legthTableAlphanumeric; i++) {
        if ( _chr == tableAlphanumeric[i]) return i;
    }

    return -1;
}

// GetNumberBits возвращает требуемое количество бит для определенной версии
int QRgen::GetNumberBits() {

    int strLength;

    if ((this->version >= 1) && (this->version <= 9))
    {
        switch (this->typeData)
        {
            case 0:
                strLength = 10;
                break;
            case 1:
                strLength = 9;
                break;
            case 2:
                strLength = 8;
                break;
            case 3:
                strLength = 8;
                break;
            default:
                strLength = 0;
                break;
        }
    }
    else if ((this->version >= 10) && (this->version <= 26))
    {
        switch (this->typeData)
        {
            case 0:
                strLength = 12;
                break;
            case 1:
                strLength = 11;
                break;
            case 2:
                strLength = 16;
                break;
            case 3:
                strLength = 10;
                break;
            default:
                strLength = 0;
                break;
        }
    }
    else if ((this->version >= 27) && (this->version <= 40))
    {
        switch (this->typeData)
        {
            case 0:
                strLength = 14;
                break;
            case 1:
                strLength = 13;
                break;
            case 2:
                strLength = 16;
                break;
            case 3:
                strLength = 12;
                break;
            default:
                strLength = 0;
                break;
        }
    }
    else
    {
        qDebug()<<"Wrong QRVersion selected. GetNumberBits";
    }

    return strLength;
}

// GetVersion возвращает номер версии QR кода
// getQRVersion
int QRgen::GetVersion() {

// массивы состоят только из одной версии коррекции ошибок - Q

    const int numeric[ConstInFunctions::characters] = {0,27,48,77,111,144,178,207,259,312,364,
                                                      427,489,580,621,703,775,876,948,1063,1159,
                                                      1224,1358,1468,1588,1718,1804,1933,2085,2181,2358,
                                                      2473,2670,2805,2949,3081,3244,3417,3599,3791,3993};

    const int alphanumeric[ConstInFunctions::characters] = {0,16,29,47,67,87,108,125,157,189,221,
                                                           259,296,352,376,426,470,531,574,644,702,
                                                           742,823,890,963,1041,1094,1172,1263,1322,1429,
                                                           1499,1618,1700,1787,1867,1966,2071,2181,2298,2420};

    const int binaryMode[ConstInFunctions::characters] = {0,11,20,32,46,60,74,86,108,130,151,
                                                          177,203,241,258,292,322,364,394,442,482,
                                                          509,565,611,661,715,751,805,868,908,982,
                                                          1030,1112,1168,1228,1283,1351,1423,1499,1579,1663};
    int version = 0;

    switch (this->typeData) {
    case 0:
        if ( this->lengthMessage > 3993 ) return version;
        for (int i = 1; i < ConstInFunctions::characters; i++) {
            if ( this->lengthMessage == numeric[i] ) {
                version = i;
                qDebug()<<"i= "<<i<<"numeric= "<<numeric[i];                    // потом можно убрать
                break;
            } else {
                if ( (this->lengthMessage > numeric[i-1]) && (this->lengthMessage <= numeric[i]) ) {
                    version = i;
                    qDebug()<<"i= "<<i<<"ближайшее подходящее numeric= "<<numeric[i];
                    break;
                }
            }
        }
        break;
    case 1:
        if ( this->lengthMessage > 2420 ) return version;
        for (int i = 1; i < ConstInFunctions::characters; i++) {
            if ( this->lengthMessage == alphanumeric[i] ) {
                version = i;
                qDebug()<<"i= "<<i<<"alphanumeric= "<<alphanumeric[i];
                break;
            } else {
                if ( (this->lengthMessage > alphanumeric[i-1]) && (this->lengthMessage <= alphanumeric[i]) ) {
                    version = i;
                    qDebug()<<"i= "<<i<<"ближайшее подходящее alphanumeric= "<<alphanumeric[i];
                    break;
                }
            }
        }
        break;
    case 2:
        if ( this->lengthMessage > 1663 ) return version;
        for (int i = 1; i < ConstInFunctions::characters; i++) {
            if ( this->lengthMessage == binaryMode[i] ) {
                version = i;
                qDebug()<<"i= "<<i<<"binaryMode= "<<binaryMode[i];
                break;
            } else {
                if ( (this->lengthMessage > binaryMode[i-1]) && (this->lengthMessage <= binaryMode[i]) ) {
                    version = i;
                    qDebug()<<"i= "<<i<<"ближайшее подходящее binaryMode= "<<binaryMode[i];
                    break;
                }
            }
        }
        break;
    default:
        break;
    }

    return version;
}
// буквено-числовое кодирование
//кодируем по 2 символа, если нечетное количество то последний символ оставляем на потом.
int QRgen::EncodeAlphanumeric() {

    int n = 0;
    if ( (this->lengthMessage % 2) != 0 ) n = 1;
    if ( this->lengthMessage < 2) return -1;

    int characterNumber = 0;
    int num = 0;
    bool isSecond = false;

    for (int i = 0; i <  this->lengthMessage - n; i += 2) {
        for ( int j = 0; j < 2; j++) {
            characterNumber = this->GetAlphanumeric(this->message.at(j+i));
            if ( isSecond == true) {                                                            // это второй символ
                num += characterNumber;
                isSecond = false;

                this->tmpStr = QString::number(num, 2);
                this->AddingZeros(0);
                this->binaryMessage += this->tmpStr;
                this->tmpStr.clear();
                break;
            }
            num = 45 * characterNumber;
            isSecond = true;
        }
    }

    if (n != 0) {
        num = this->GetAlphanumeric(this->message.at(this->lengthMessage-1));
        this->tmpStr = QString::number(num, 2);
        this->AddingZeros(n);
        this->binaryMessage += this->tmpStr;

    }

    return 0;
}

// числовое кодирование
int QRgen::EncodeNumeric() {

    int multi = 0;
    // определяем кратность трем, сколько в конце символов
    if ( (this->lengthMessage % 3) == 0) {

    } else {
        if ( (this->lengthMessage - 1) % 3 == 0) {
            multi = 1;
        } else {
            if ( (this->lengthMessage - 2) % 3 == 0) {
                multi = 2;
            } else {
                qDebug()<<"число слишком короткое";
                return -1;
            }
        }
    }

    int num = 0;
    this->tmpStr.clear();
    // кодируется с шагом три, последние символы оставляем на потом
    for ( int i = 0; i < this->lengthMessage - multi; i += 3) {
        for ( int j = 0; j < 3; j++) {
            num++;
            this->tmpStr += this->message.at(j+i);
            if (num == 3) { num = 0; break; }
        }
        num = this->tmpStr.toInt(); // num использую чтобы не объявлять еще одну переменную просто ради экономии, если будет замечание уберу)
        this->tmpStr = QString::number(num, 2);
        this->AddingZeros(0);                   // добавить нули если нужно

        num = 0;
        qDebug()<<"tmpStr= "<<this->tmpStr;
        this->binaryMessage += this->tmpStr;
        this->tmpStr.clear();
    }
    if (multi != 0) {

        if (multi == 1) {

            this->tmpStr = this->message.at(this->lengthMessage - 1);
            num = this->tmpStr.toInt();
            this->tmpStr = QString::number(num, 2);
            this->AddingZeros(multi);
            num = 0;
            this->binaryMessage += this->tmpStr;
            this->tmpStr.clear();
        } else {

            this->tmpStr = this->message.at(this->lengthMessage - 2);
            this->tmpStr += this->message.at(this->lengthMessage - 1);
            num = this->tmpStr.toInt();
            this->tmpStr = QString::number(num, 2);
            this->AddingZeros(multi);
            num = 0;
            this->binaryMessage += this->tmpStr;
            this->tmpStr.clear();
        }

    }
    return 0;
}

// побитовое кодирование
int QRgen::EncodeBinaryMode() {

    this->tmpStr.clear();
    int characterNumber = 0;
    for (int i = 0; i < this->lengthMessage; i++){
        characterNumber = this->message.at(i).unicode();
        //qDebug()<<"characterNumber= "<<characterNumber;
        this->tmpStr = QString::number(characterNumber, 2);
        // добавление недостающих нулей
        if (this->tmpStr.size() < 8) {
            this->binaryMessage += this->addZero(this->tmpStr);
            /*int mis = 8 - this->tmpStr.size();
            QString s = "";
            s += QString(mis, '0');
            this->tmpStr.prepend(s);*/
        }else{
            this->binaryMessage += this->tmpStr;
        }
    }
    return 0;
}

// если мало бит, то добавляет нули
int QRgen::AddingZeros(const int &multi) {

    int n = 0;
    if (this->typeData == 0) {

        switch (multi) {
        case 0:
            n = 10;
            break;
        case 1:
            n = 4;
            break;
        case 2:
            n = 7;
            break;
        default:
            break;
        }
    } else if (this->typeData == 1) {

        switch (multi) {
        case 0:
            n = 11;
            break;
        case 1:
            n = 6;      // последний один символ кодируется как 6 бит
            break;
        default:
            break;
        }
    }
    // добавление недостающих нулей
    if (this->tmpStr.size() < n) {
        int mis = n - this->tmpStr.size();
        QString s = "";
        s += QString(mis, '0');
        this->tmpStr.prepend(s);
    }

    return 0;
}

// корректировка размера по таблице. смотрим сколько бит в версии, если не хватает добавляем.
int QRgen::LengthCorrection() {

    const int dataBits[ConstInFunctions::bits] = {0,104,176,272,384,496,608,704,880,1056,1232,
                                                  1440,1648,1952,2088,2360,2600,2936,3176,3560,3880,
                                                  4096,4544,4912,5312,5744,6032,6464,6968,7288,7880,
                                                  8264,8920,9368,9848,10288,10832,11408,12016,12656,13328};


    //this->binaryMessage.remove(QChar(' '));     // TODO: потом удалить эту строку и добавление пробелов в программе !!!!!!!!!!!!!!!!!!
    int num = this->binaryMessage.size();

    if( num > dataBits[this->version] ) return -1;

    if ( dataBits[this->version] - num == 4) {
        this->binaryMessage += "0000";
        return 0;
    } else {
        if ( dataBits[this->version] - num < 4 ) {
            this->binaryMessage += QString(dataBits[this->version] - num, '0');
            return 0;
        } else {    // если остаток больше 4, то нужно корректировать длину
            this->binaryMessage += "0000";
            num += 4;   // прибавляем 4 т.к. добавили 4 нуля
        }
    }

    // разбиваем по 8 бит, если не кратно 8, то добавляем нули
    int n = num / 8;
    n = n * 8;
    n = num - n;
    n = 8 - n;
    this->binaryMessage += QString(n, '0');
    num += n;
    const QString _1 = "11101100";
    const QString _2 = "00010001";
    n = dataBits[this->version] - num;
    n = n / 8;
    for (int i = 0; i < n; i++) {
        if ( i % 2 == 0) {
            this->binaryMessage += _1;
        } else {
            this->binaryMessage += _2;
        }
    }

    if ( this->binaryMessage.size() > dataBits[this->version]) return -1;
    return 0;
}

// генерация кода Рида-Соломона
int QRgen::ReedSolomon() {
    // x^8 + x^4 + x^3 + x^2 + 1 -> 100011101 - Primitive polynomial
    const int qrCodeField = 0x011D; // 285
    const int fieldCount = 256;

    // TODO: здесь должна быть обработка ошибок при выделении памяти
    int* logTable = new int[fieldCount];            // таблица поля Галуа
    int* antiLogTable = new int[fieldCount];        // обратная таблица поля Галуа

    // step 1 генерация полей галуа
    int mask = 1;                          // this is mask

    for (int i = 0; i < fieldCount; i++) {
        logTable[i] = mask;

        mask = mask << 1;

        if (mask >= fieldCount) {
            mask ^= qrCodeField;
            mask &= fieldCount - 1;
        }
    }
    antiLogTable[0] = -1;
    logTable[255] = 0;      // данные из разных книг различаются, возможно нужно указать число больше 256. (32647)

    // обратное поле
    for (int i = 0; i < fieldCount - 1; i++) {
        antiLogTable[logTable[i]] = i;
    }

    // количество блоков в группах
    const short int codeBlocks[200] = {
        13, 1, 13, 0, 0,
        22, 1, 22, 0, 0,
        18, 2, 17, 0, 0,
        26, 2, 24, 0, 0,
        18, 2, 15, 2, 16,
        24, 4, 19, 0, 0,
        18, 2, 14, 4, 15,
        22, 4, 18, 2, 19,
        20, 4, 16, 4, 17,
        24, 6, 19, 2, 20,
        28, 4, 22, 4, 23,
        26, 4, 20, 6, 21,
        24, 8, 20, 4, 21,
        20, 11, 16, 5, 17,
        30, 5, 24, 7, 25,
        24, 15, 19, 2, 20,
        28, 1, 22, 15, 23,
        28, 17, 22, 1, 23,
        26, 17, 21, 4, 22,
        30, 15, 24, 5, 25,
        28, 17, 22, 6, 23,
        30, 7, 24, 16, 25,
        30, 11, 24, 14, 25,
        30, 11, 24, 16, 25,
        30, 7, 24, 22, 25,
        28, 28, 22, 6, 23,
        30, 8, 23, 26, 24,
        30, 4, 24, 31, 25,
        30, 1, 23, 37, 24,
        30, 15, 24, 25, 25,
        30, 42, 24, 1, 25,
        30, 10, 24, 35, 25,
        30, 29, 24, 19, 25,
        30, 44, 24, 7, 25,
        30, 39, 24, 14, 25,
        30, 46, 24, 10, 25,
        30, 49, 24, 10, 25,
        30, 48, 24, 14, 25,
        30, 43, 24, 22, 25,
        30, 34, 24, 34, 25
    };

    // берем данные из таблицы
    int codewords = codeBlocks[(this->version - 1) * 5];    // number of errors - размер блока под коррекцию ошибок
    int countGroup_1 = codeBlocks[(this->version - 1) * 5 + 1];
    int countFirstBlock = codeBlocks[(this->version - 1) * 5 + 2];
    int countGroup_2 = codeBlocks[(this->version - 1) * 5 + 3];
    int countSecondBlock = codeBlocks[(this->version - 1) * 5 + 4];

    // step 2 генерация порождающего полинома
    const int nn = 255;      // 2**8-1 длина кодового слова
    int kk = nn - codewords;   // kk = nn-codewords Degree of g(x) = 2*codewords - информационное слово 242
    int gg[codewords + 1];      // таблица порождающего полинома
    int b0 = 0;         // g(x) имеет корни g(x)**b0, g(x)**(b0+1), ... ,g(x)^(b0+2*codewords-1)

    gg[0] = logTable[b0];
    gg[1] = 1;                                 // g(x) = (X+g(x)**b0) - инициализация
    for (int i=2; i <= codewords; i++) {
        gg[i] = 1;
        // заполнение порождающего полинома (gg[0]+gg[1]*x + ... +gg[i]x^i) до (g(x)**(b0+i-1) + x)
        for (int j=i-1; j>0; j--)
            if (gg[j] != 0)  gg[j] = gg[j-1]^ logTable[((antiLogTable[gg[j]])+b0+i-1)%nn];
            else gg[j] = gg[j-1];

        gg[0] = logTable[((antiLogTable[gg[0]])+b0+i-1)%nn];     /* gg[0] никогда не равен нулю */
    }
    // порождающий полином gg в удобном виде
    for (int i=0; i </*=*/ codewords; i++) gg[i] = antiLogTable[gg[i]];

    // массив основных данных, массив кода корректировки и массивы чередования символов
    int data[kk], bb[codewords];

    // TODO: здесь должна быть обработка ошибок при выделении памяти
    // Создаются 4 разных массива для работы с определенным блоком,
    // так как методы чередования символов разные для почти каждой версии
    int** arrayFirstGroup = new int*[countGroup_1];
    for (int i = 0; i < countGroup_1; i++) {
        arrayFirstGroup[i] = new int[countFirstBlock];
    }
    int** arraySecondGroup = new int*[countGroup_2];
    for (int i = 0; i < countGroup_2; i++) {
        arraySecondGroup[i] = new int[countSecondBlock];
    }

    int** correctArrayFirstGroup = new int*[countGroup_1];
    for (int i = 0; i < countGroup_1; i++) {
        correctArrayFirstGroup[i] = new int[codewords];
    }
    int** correctArraySecondGroup = new int*[countGroup_2];
    for (int i = 0; i < countGroup_2; i++) {
        correctArraySecondGroup[i] = new int[codewords];
    }

    // инициализация
    for (int i = 0; i < countGroup_1; i++) {
        for (int j = 0; j < countFirstBlock; j++) {
            arrayFirstGroup[i][j] = 0;
        }
    }
    for (int i = 0; i < countGroup_2; i++) {
        for (int j = 0; j < countSecondBlock; j++) {
            arraySecondGroup[i][j] = 0;
        }
    }

    for (int i = 0; i < countGroup_1; i++) {
        for (int j = 0; j < codewords; j++) {
            correctArrayFirstGroup[i][j] = 0;
        }
    }
    for (int i = 0; i < countGroup_2; i++) {
        for (int j = 0; j < codewords; j++) {
            correctArraySecondGroup[i][j] = 0;
        }
    }

    bool isEnd = false;
    int tempCountGroup = countGroup_1;      // инициализайия первой группой
    int tempCountBlock = countFirstBlock;   // инициализайия первым блоком
    short int bloc = tempCountBlock * 8;    // для взятия каждого блока
    short int counterBloc = 0;

    QString tempByteString = "";
    int numByte = 0;
    bool ok;
    int positionData = tempCountBlock - 1;

    do{
        // step 3 подготовка сообщения для кодирования
        for  (int i = 0; i < kk; i++)   data[i] = 0;

        // цикл кодирования по блокам
        for (int currentGroup = 0; currentGroup < tempCountGroup; currentGroup++){
            for (int t = counterBloc; t < bloc; t += 8) {   // взятие из сообщения очередного блока
                for (int jj = 0; jj < 8; jj++) {
                    tempByteString += this->binaryMessage.at(jj+t);
                }

                numByte = tempByteString.toInt(&ok,2);
                data[positionData] = numByte;

                // заполняем только нужную группу
                if (!isEnd){
                    arrayFirstGroup[currentGroup][positionData] = numByte;    // заполнение first group
                }
                if (isEnd){
                    arraySecondGroup[currentGroup][positionData] = numByte;    // заполнение second group
                }

                positionData--;
                tempByteString.clear();
            }
            positionData = tempCountBlock - 1;  // сбрасываем счетчик для текущего блока

            // step 4 кодирование сообщения
            int feedback = 0;

            for (int i = 0; i < codewords; i++) bb[i] = 0;

            for (int i=kk-1; i>=0; i--) {
                feedback = antiLogTable[data[i]^bb[codewords-1]];

                if (feedback != -1) {                               // не равно нулю в таблице
                    for (int j=codewords-1; j>0; j--)
                        if (gg[j] != -1)
                            bb[j] = bb[j-1]^logTable[(gg[j]+feedback)%nn];
                        else
                            bb[j] = bb[j-1];
                    bb[0] = logTable[(gg[0]+feedback)%nn];
                }
                else // слагаемое равно нулю, производится однобайтовый сдвиг
                {
                    for (int j=codewords-1; j>0; j--) bb[j] = bb[j-1];
                    bb[0] = 0;
                }
            }
            if (!isEnd){
                for (int i = 0; i < codewords; i++){
                    correctArrayFirstGroup[currentGroup][i] = bb[i];    // заполнение correction first group
                }
            }

            if (isEnd){
                for (int i = 0; i < codewords; i++){
                    correctArraySecondGroup[currentGroup][i] = bb[i];    // заполнение correction second group
                }
            }
            counterBloc = bloc;
            bloc += tempCountBlock * 8;     // наращиваем для получения следующего блока
        }

        if(isEnd == true) break;
        if (countGroup_2 != 0){             // необходимо  кодирование второй группы

            bloc -= tempCountBlock * 8;     // откатываем назад, убираем лишнее приращение
            tempCountGroup = countGroup_2;
            tempCountBlock = countSecondBlock;  // теперь будут обрабатываться следующие блоки
            bloc += tempCountBlock * 8;

            positionData = countSecondBlock - 1;
            isEnd = true;  // обработка второй группы
        } else break;
    }while(true);

    QString strByte = "";
    QString tempStrWord = "";
    short int dig = 0;
    if(countSecondBlock == 0){
        tempCountBlock = countFirstBlock;       // инициализайия 1 блоком
    }else{
        tempCountBlock = countSecondBlock;      // инициализайия 2 блоком
        dig = 1;
    }

    for (int i = tempCountBlock -1; i >= 0; i--){
        if(i != 0){
            for (int firstGroup = 0; firstGroup < countGroup_1; firstGroup++){

                strByte = QString::number(arrayFirstGroup[firstGroup][i-dig], 2);
                // добавление недостающих нулей
                if (strByte.size() < 8) {
                    tempStrWord += this->addZero(strByte);
                }else{
                    tempStrWord += strByte;
                }
            }
            this->finishedBinaryMessage += tempStrWord;
            tempStrWord.clear();
        }else{
            if(countFirstBlock > countSecondBlock){
                for (int firstGroup = 0; firstGroup < countGroup_1; firstGroup++){

                    strByte = QString::number(arrayFirstGroup[firstGroup][i-dig], 2);
                    // добавление недостающих нулей
                    if (strByte.size() < 8) {
                        tempStrWord += this->addZero(strByte);
                    }else{
                        tempStrWord += strByte;
                    }
                }
                this->finishedBinaryMessage += tempStrWord;
                tempStrWord.clear();
            }
        }

        tempStrWord.clear();
        if(countSecondBlock != 0){
            for (int secondGroup = 0; secondGroup < countGroup_2; secondGroup++){

                strByte = QString::number(arraySecondGroup[secondGroup][i], 2);
                // добавление недостающих нулей
                if (strByte.size() < 8) {
                    tempStrWord += this->addZero(strByte);
                }else{
                    tempStrWord += strByte;
                }
            }
            this->finishedBinaryMessage += tempStrWord;
            tempStrWord.clear();
        }
    }

    tempStrWord.clear();
    for (int i = codewords -1; i >= 0; i--){

            for (int firstGroup = 0; firstGroup < countGroup_1; firstGroup++){

                strByte = QString::number(correctArrayFirstGroup[firstGroup][i], 2);
                // добавление недостающих нулей
                if (strByte.size() < 8) {
                    tempStrWord += this->addZero(strByte);
                }else{
                    tempStrWord += strByte;
                }
            }
            this->finishedBinaryMessage += tempStrWord;
            tempStrWord.clear();

        if(countSecondBlock != 0){
            for (int secondGroup = 0; secondGroup < countGroup_2; secondGroup++){

                strByte = QString::number(correctArraySecondGroup[secondGroup][i], 2);
                // добавление недостающих нулей
                if (strByte.size() < 8) {
                    tempStrWord += this->addZero(strByte);
                }else{
                    tempStrWord += strByte;
                }
            }
            this->finishedBinaryMessage += tempStrWord;
            tempStrWord.clear();
        }
    }
    // TODO: добавить недостающте нули

    // освобождение памяти
    delete[] logTable;
    delete[] antiLogTable;

    for (int i = 0; i < countGroup_1; i++) {
        delete[] arrayFirstGroup[i];
    }
    for (int i = 0; i < countGroup_2; i++) {
        delete[] arraySecondGroup[i];
    }

    for (int i = 0; i < countGroup_1; i++) {
        delete[] correctArrayFirstGroup[i];
    }
    for (int i = 0; i < countGroup_2; i++) {
        delete[] correctArraySecondGroup[i];
    }
    delete arrayFirstGroup, arraySecondGroup, correctArrayFirstGroup, correctArraySecondGroup;
    return 0;
}

// только добавление нулей
const QString QRgen::addZero(const QString &str){

    QString strByte = str;
    int mis = 8 - str.size();
    QString s = "";
    s += QString(mis, '0');
    return strByte.prepend(s);
}

// расчет по выбранной маске
int QRgen::ChoiceMask(const int &n, const int &i, const int &j) {

    switch (n) {
    case 0:
        return (j + i) % 2;
    case 1:
        return (j % 2);
    case 2:
        return (i % 3);
    case 3:
        return (j + i) % 3;
    case 4:
        return ((j / 2) + (i / 3)) % 2;
    case 5:
        return ((j * i) % 2) + ((j * i) % 3);
    case 6:
        return (((j * i) % 2) + ((j * i) % 3)) % 2;
    case 7:
        return (((j + i) % 2) + ((j * i) % 3)) % 2;
    default:
        return 1;
    }
}

// заполнение матрицы данными
int QRgen::ImageGeneration(const int &width, const int &hight) {

    int range = (this->version - 1) * 4 + 21;       // размер матрицы
    // создание матрицы
    int** qrMatrix = new int*[range];
    int** qrMatrixCopy = new int*[range];           // копия матрицы для подбора маски
    for (int i = 0; i < range; i++) {
        qrMatrix[i] = new int[range];
        qrMatrixCopy[i] = new int[range];
    }

    for (int i = 0; i < range; i++) {
        for (int j = 0; j < range; j++) {
            qrMatrix[i][j] = 9;
            qrMatrixCopy[i][j] = 9;
        }
    }

    int r = range - 8;
    // блокирование бит, созданиe ключей для обнаружения
    // left
    int count = 8;
    int num = 0;
    int sum = 0;
    while (true) {
        for (int i = 0 + sum; i < count; i++)
            for (int j = 0 + sum; j < count; j++) {
                qrMatrix[i][j] = num;
            }
        if (sum == 2) break;
        if (count == 8) {
            count--;
            num = 1;
        } else {
            if (count == 7) {
                count--;
                num = 0;
                sum++;
            } else {
                if (count == 6) {
                    count--;
                    num = 1;
                    sum++;
                }
            }
        }
    }

    // right
    count = 8;
    num = 0;
    sum = 0;
    int sum_2 = 0;
    while (true) {
        for (int i = r + sum; i < range - sum_2; i++)
            for (int j = 0 + sum_2; j < count; j++) {
                qrMatrix[i][j] = num;
            }
        if (sum == 3) break;
        if (count == 8) {
            count--;
            sum++;
            num = 1;
        } else {
            if (count == 7) {
                count--;
                num = 0;
                sum++;
                sum_2++;
            } else {
                if (count == 6) {
                    count--;
                    num = 1;
                    sum++;
                    sum_2++;
                }
            }
        }
    }

    // down
    count = 8;
    num = 0;
    sum = 0;
    sum_2 = 0;
    while (true) {
        for (int i = 0 + sum_2; i < count; i++)
            for (int j = r + sum; j < range - sum_2; j++) {
                qrMatrix[i][j] = num;
            }
        if (sum == 3) break;
        if (count == 8) {
            count--;
            num = 1;
            sum++;
        } else {
            if (count == 7) {
                count--;
                num = 0;
                sum++;
                sum_2++;
            } else {
                if (count == 6) {
                    count--;
                    num = 1;
                    sum++;
                    sum_2++;
                }
            }
        }
    }

    // Black pixel
    qrMatrix[8][r] = 1;

    // блокирование бит type information
    // horizontal
    for (int i = 0; i < range; i++) {
        qrMatrix[i][8] = 1;
        if (i == 5) i++;
        if (i == 7) i += r - 8;
    }
    // vertical
    for (int j = 0; j < range; j++) {
        qrMatrix[8][j] = 1;
        if (j == 5) j++;
        if (j == 8) j += r - 8;
    }

    // Version Information
    // для версий не ниже седьмой
    if (this->version >= 7) {
        const QString versionInfo[34] = {
            "001010010011111000",
            "000111101101000100",
            "100110010101100100",
            "011001011001010100",
            "011011111101110100",
            "001000110111001100",
            "111000100001101100",
            "010110000011011100",
            "000101001001111100",
            "000111101101000010",
            "010111010001100010",
            "111010000101010010",
            "001001100101110010",
            "011001011001001010",
            "011000001011101010",
            "100100110001011010",
            "000110111111111010",
            "001000110111000110",
            "000100001111100110",
            "110101011111010110",
            "000001110001110110",
            "010110000011001110",
            "001111110011101110",
            "101011101011011110",
            "000000101001111110",
            "101010111001000001",
            "000001111011100001",
            "010111010001010001",
            "011111001111110001",
            "110100001101001001",
            "001110100001101001",
            "001001100101011001",
            "010000010101111001",
            "100101100011000101"
        };
        QString tempVersionInfo = versionInfo[this->version - 7];
        int countMask = 0;
        // right
        for (int j = 0; j < 6; j++)
            for (int i = range - 11; i < range - 8; i++) {
                if (tempVersionInfo.at(countMask) == '1') {
                    qrMatrix[i][j] = 1;
                } else qrMatrix[i][j] = 0;
                countMask++;
            }
        countMask = 0;
        // down
        for (int i = 0; i < 6; i++)
            for (int j = range - 11; j < range - 8; j++) {
                if (tempVersionInfo.at(countMask) == '1') {
                    qrMatrix[i][j] = 1;
                } else qrMatrix[i][j] = 0;
                countMask++;
            }
    }
    // Adjustment pattern
    const short int alignmentPos[39][7] = {
        {6, 18, 0, 0, 0, 0, 0},
        {6, 22, 0, 0, 0, 0, 0},
        {6, 26, 0, 0, 0, 0, 0},
        {6, 30, 0, 0, 0, 0, 0},
        {6, 34, 0, 0, 0, 0, 0},
        {6, 22, 38, 0, 0, 0, 0},
        {6, 24, 42, 0, 0, 0, 0},
        {6, 26, 46, 0, 0, 0, 0},
        {6, 28, 50, 0, 0, 0, 0},
        {6, 30, 54, 0, 0, 0, 0},
        {6, 32, 58, 0, 0, 0, 0},
        {6, 34, 62, 0, 0, 0, 0},
        {6, 26, 46, 66, 0, 0, 0},
        {6, 26, 48, 70, 0, 0, 0},
        {6, 26, 50, 74, 0, 0, 0},
        {6, 30, 54, 78, 0, 0, 0},
        {6, 30, 56, 82, 0, 0, 0},
        {6, 30, 68, 86, 0, 0, 0},
        {6, 34, 62, 90, 0, 0, 0},
        {6, 28, 50, 72, 94, 0, 0},
        {6, 26, 50, 74, 98, 0, 0},
        {6, 30, 54, 78, 102, 0, 0},
        {6, 28, 54, 80, 106, 0, 0},
        {6, 32, 58, 84, 110, 0, 0},
        {6, 30, 58, 86, 114, 0, 0},
        {6, 34, 62, 90, 118, 0, 0},
        {6, 26, 50, 74, 98, 122, 0},
        {6, 30, 54, 78, 102, 126, 0},
        {6, 26, 52, 78, 104, 130, 0},
        {6, 30, 56, 82, 108, 134, 0},
        {6, 34, 60, 86, 112, 138, 0},
        {6, 30, 58, 86, 114, 142, 0},
        {6, 34, 62, 90, 118, 146, 0},
        {6, 30, 54, 78, 102, 126, 150},
        {6, 24, 50, 76, 102, 128, 154},
        {6, 28, 54, 80, 106, 132, 158},
        {6, 32, 58, 84, 110, 136, 162},
        {6, 26, 54, 82, 110, 138, 166},
        {6, 30, 58, 86, 114, 142, 170}
    };
    // блокирование бит Adjustment pattern
    const int versia = this->version;

    if (versia != 1) {

        for (int i = 0; i < 7; i++) {
            if ( alignmentPos[versia - 2][i] == 0 ) continue;

            for (int j = 0; j < 7; j++) {
                if ( alignmentPos[versia - 2][j] == 0 ) continue;
                if (qrMatrix[ alignmentPos[ versia - 2 ][i]][ alignmentPos[ versia - 2 ][j]] == 9 ) {

                    for (int x = alignmentPos[ versia - 2 ][i] - 2; x < alignmentPos[ versia - 2 ][i] + 3; x++)
                        for (int y = alignmentPos[ versia - 2 ][j] - 2; y < alignmentPos[ versia - 2 ][j] + 3; y++) {
                            qrMatrix[x][y] = 1;
                        }
                    for (int x = alignmentPos[ versia - 2 ][i] - 1; x < alignmentPos[ versia - 2 ][i] + 2; x++)
                        for (int y = alignmentPos[ versia - 2 ][j] - 1; y < alignmentPos[ versia - 2 ][j] + 2; y++) {
                            qrMatrix[x][y] = 0;
                        }
                    qrMatrix[ alignmentPos[ versia - 2 ][i]][ alignmentPos[ versia - 2 ][j]] = 1;
                }
            }
        }
    }

    // Timing pattern
    for (int i = 8; i < r; i++) {
        if ((i % 2) == 0) {
            qrMatrix[i][6] = 1;
        } else qrMatrix[i][6] = 0;
    }
    for (int j = 8; j < r; j++) {
        if ((j % 2) == 0) {
            qrMatrix[6][j] = 1;
        } else qrMatrix[6][j] = 0;
    }

    // Type Information
    const QString typeInf[8] = {
        "011010101011111",
        "011000001101000",
        "011111100110001",
        "011101000000110",
        "010010010110100",
        "010000110000011",
        "010111011011010",
        "010101111101101"
    };

    // models mask for rule 3
    const int rule3Model1[15] = {0,0,0,0,1,0,1,1,1,0,1,0,0,0,0};
    const int rule3Model2[11] = {1,0,1,1,1,0,1,0,0,0,0};
    const int rule3Model3[11] = {0,0,0,0,1,0,1,1,1,0,1};

    // выбор оптимальной маски и заполнение данными
    int viewMask = 0;                                   // счетчик для выбора маски
    int optimalMask = 0;
    int optimalPenalty = 0;
    const float countPixels = range * range;
    qDebug()<<"выбор оптимальной маски и заполнение данными";
    for (int nMask = 0; nMask < 9; nMask++) {           // проход по каждой из 8 масок и выбор окончательного варианта

        int penalty = 0;                                // сумма нарушений
        int column = range - 1;
        int countBin = 0;
        int zeroMask = 0;                               // возвращаемое значение из формулы маски
        int approaches = column/4;
        // восстановление первоначального состояния матрицы-копии
        for (int i = 0; i < range; i++) {
            for (int j = 0; j < range; j++) {
                qrMatrixCopy[i][j] = qrMatrix[i][j];
            }
        }

        // type information по выбраной маске
        QString mask = typeInf[viewMask];
        // Type information
        // horizontal
        int countMask = 0;
        for (int i = 0; i < range; i++) {
            if (mask.at(countMask) == '1') {
                qrMatrixCopy[i][8] = 1;
            } else qrMatrixCopy[i][8] = 0;

            countMask++;
            if (i == 5) i++;
            if (i == 7) i += r - 8;
        }
        // vertical
        countMask = 14;
        for (int j = 0; j < range; j++) {
            if (mask.at(countMask) == '1') {
                qrMatrixCopy[8][j] = 1;
            } else qrMatrixCopy[8][j] = 0;

            countMask--;
            if (j == 5) j++;
            if (j == 8) j += r - 8;
        }

        // заполняем матрицу данными
        for (int k = 0; k < approaches; k++) {          // количество проходов
            //qDebug()<<"снизу вверх";
            for (int j = range; j >= 0; j--) {
                if ((qrMatrixCopy[column][j] != 9) && (qrMatrixCopy[column - 1][j] != 9)) {/*qrMatrixCopy[column][j] = 8;
                    qrMatrixCopy[column-1][j] = 7;*/continue;}
                // начинаем обход Adjustment pattern
                if ((qrMatrixCopy[column][j] != 9) && (qrMatrixCopy[column - 1][j] == 9)) {

                    zeroMask = this->ChoiceMask(viewMask, column - 1, j);
                    if (zeroMask == 0) {
                        if (this->finishedBinaryMessage.at(countBin) == '1') qrMatrixCopy[column - 1][j] = 0;
                        if (this->finishedBinaryMessage.at(countBin) == '0') qrMatrixCopy[column - 1][j] = 1;
                    } else {
                        if (this->finishedBinaryMessage.at(countBin) == '1') qrMatrixCopy[column - 1][j] = 1;
                        if (this->finishedBinaryMessage.at(countBin) == '0') qrMatrixCopy[column - 1][j] = 0;
                    }
                    countBin++;
                    continue;
                }

                for (int i = column; i >= column - 1; i--) {
                    zeroMask = this->ChoiceMask(viewMask, i, j);
                    if (zeroMask == 0) {
                        if (this->finishedBinaryMessage.at(countBin) == '1') qrMatrixCopy[i][j] = 0;
                        if (this->finishedBinaryMessage.at(countBin) == '0') qrMatrixCopy[i][j] = 1;
                    } else {
                        if (this->finishedBinaryMessage.at(countBin) == '1') qrMatrixCopy[i][j] = 1;
                        if (this->finishedBinaryMessage.at(countBin) == '0') qrMatrixCopy[i][j] = 0;
                    }
                    countBin++;
                }

            }
            if (column == 8) column--;
            column -= 2;
            //qDebug()<<"сверху вниз";
            for (int j = 0; j < range; j++) {
                if ((qrMatrixCopy[column][j] != 9) && (qrMatrixCopy[column - 1][j] != 9)) {/*qrMatrixCopy[column][j] = -5;
                    qrMatrixCopy[column-1][j] = -6;*/continue;}
                // начинаем обход Adjustment pattern
                if ((qrMatrixCopy[column][j] != 9) && (qrMatrixCopy[column - 1][j] == 9)) {

                    zeroMask = this->ChoiceMask(viewMask, column - 1, j);
                    if (zeroMask == 0) {
                        if (this->finishedBinaryMessage.at(countBin) == '1') qrMatrixCopy[column - 1][j] = 0;
                        if (this->finishedBinaryMessage.at(countBin) == '0') qrMatrixCopy[column - 1][j] = 1;
                    } else {
                        if (this->finishedBinaryMessage.at(countBin) == '1') qrMatrixCopy[column - 1][j] = 1;
                        if (this->finishedBinaryMessage.at(countBin) == '0') qrMatrixCopy[column - 1][j] = 0;
                    }
                    countBin++;
                    continue;
                }

                for (int i = column; i >= column - 1; i--) {
                    zeroMask = this->ChoiceMask(viewMask, i, j);
                    if (zeroMask == 0) {
                        if (this->finishedBinaryMessage.at(countBin) == '1') qrMatrixCopy[i][j] = 0;
                        if (this->finishedBinaryMessage.at(countBin) == '0') qrMatrixCopy[i][j] = 1;
                    } else {
                        if (this->finishedBinaryMessage.at(countBin) == '1') qrMatrixCopy[i][j] = 1;
                        if (this->finishedBinaryMessage.at(countBin) == '0') qrMatrixCopy[i][j] = 0;
                    }
                    countBin++;
                }
            }
            column -= 2;
        }
        // теперь необходимо рассчитать значение маски по 4 алгоритмам и запомнить результат
        if (viewMask != 8) {    // чтобы не осуществлялся последний проход

            // rule 1
            // horizontal
            for (int j = 0; j < range; j++) {
                for (int i = 0; i < range - 4; i++ ) {
                    if ((qrMatrixCopy[i][j] == qrMatrixCopy[i+1][j])
                            && (qrMatrixCopy[i][j] == qrMatrixCopy[i+2][j])
                            && (qrMatrixCopy[i][j] == qrMatrixCopy[i+3][j])
                            && (qrMatrixCopy[i][j] == qrMatrixCopy[i+4][j])) {

                        int marker = qrMatrixCopy[i][j];                // маркер - 0 или 1
                        penalty += 3;
                        for (int next = i+5; next < range; next++) {
                            if (qrMatrixCopy[next][j] == marker) {
                                penalty++;
                                i = next-1;
                            } else {
                                i = next-1;
                                break;
                            }
                        }
                    }
                }
            }
            // vertical
            for (int i = 0; i < range; i++) {
                for (int j = 0; j < range - 4; j++ ) {
                    if ((qrMatrixCopy[i][j] == qrMatrixCopy[i][j+1])
                            && (qrMatrixCopy[i][j] == qrMatrixCopy[i][j+2])
                            && (qrMatrixCopy[i][j] == qrMatrixCopy[i][j+3])
                            && (qrMatrixCopy[i][j] == qrMatrixCopy[i][j+4])) {

                        int marker = qrMatrixCopy[i][j];                // маркер - 0 или 1
                        penalty += 3;
                        for (int next = j+5; next < range; next++) {
                            if (qrMatrixCopy[i][next] == marker) {
                                penalty++;
                                j = next-1;
                            } else {
                                j = next-1;
                                break;
                            }
                        }
                    }
                }
            }

            // rule 2
            for (int j = 0; j < range - 1; j++) {
                for (int i = 0; i < range - 1; i++ ) {
                    int marker = qrMatrixCopy[i][j];                // маркер - 0 или 1
                    if ((qrMatrixCopy[i][j] == marker)
                            && (qrMatrixCopy[i+1][j] == marker)
                            && (qrMatrixCopy[i][j+1] == marker)
                            && (qrMatrixCopy[i+1][j+1] == marker)) {
                        penalty += 3;
                    }
                }
            }

            // rule 3
            // horizontal
            int match = 0;
            for (int j = 0; j < range; j++) {
                for (int i = 0; i < range - 14; i++ ) {
                    for (int rule = 0; rule < 15; rule++) {
                        if (rule3Model1[rule] == qrMatrixCopy[i+rule][j]) match++;  // сверяем элементы маски
                    }
                    // если совпала вся строка
                    if (match == 15) {
                        penalty += 40;
                    }
                    match = 0;
                }
            }
            int match2 = 0;
            for (int j = 0; j < range; j++) {
                for (int i = 0; i < range - 10; i++ ) {
                    for (int rule = 0; rule < 11; rule++) {
                        if (rule3Model2[rule] == qrMatrixCopy[i+rule][j]) match++;  // сверяем элементы маски
                        if (rule3Model3[rule] == qrMatrixCopy[i+rule][j]) match2++;
                    }
                    // если совпала вся строка
                    if ((match == 11) || (match2 == 11)) {
                        penalty += 40;
                    }
                    match = 0;
                    match2 = 0;
                }
            }
            // vertical
            for (int i = 0; i < range; i++) {
                for (int j = 0; j < range - 14; j++ ) {
                    for (int rule = 0; rule < 15; rule++) {
                        if (rule3Model1[rule] == qrMatrixCopy[i][j+rule]) match++;  // сверяем элементы маски
                    }
                    // если совпала вся строка
                    if (match == 15) {
                        penalty += 40;
                    }
                    match = 0;
                }
            }
            for (int i = 0; i < range; i++) {
                for (int j = 0; j < range - 10; j++ ) {
                    for (int rule = 0; rule < 11; rule++) {
                        if (rule3Model2[rule] == qrMatrixCopy[i][j+rule]) match++;  // сверяем элементы маски
                        if (rule3Model3[rule] == qrMatrixCopy[i][j+rule]) match2++;
                    }
                    // если совпала вся строка
                    if ((match == 11) || (match2 == 11)) {
                        penalty += 40;
                    }
                    match = 0;
                    match2 = 0;
                }
            }
            // rule 4
            int countBlackPixels = 0;
            for (int j = 0; j < range; j++) {
                for (int i = 0; i < range; i++ ) {
                    if (qrMatrixCopy[i][j] == 1) countBlackPixels++;
                }
            }
            float floatValue = float((countBlackPixels / countPixels * 100) - 50);
            int intValue = floatValue;
            if (intValue != 0) {
                if (intValue < 0) intValue *= -1;
                floatValue = float(intValue) / 5;
                intValue = floatValue * 10;
                penalty += intValue;
            }

        }
        if (viewMask == 0) optimalPenalty = penalty;
        if (penalty <= optimalPenalty) {
            optimalMask = viewMask;
            optimalPenalty = penalty;   // выбираем лучшую маску
        }
        viewMask++;         // выбираем следующую маску
        if (viewMask == 8) viewMask = optimalMask; // последняя генерация по оптимальной маске
    }

    // image
    QPainter paint;
    // подгоняем размеры
    float w = width;
    float h = hight;
    float ran = range;
    float balance = float(width / ran);
    int balanceInt = width / ran;
    if (balance != 0) {
        if(balance > 4){
            balanceInt *= ran;
            balanceInt = width - balanceInt;
            w = width - balanceInt;
        }else{
            w = ran * 4;
        }
    }

    balance = float(hight / ran);
    balanceInt = hight / ran;
    if (balance != 0) {
        if(balance > 4){
            balanceInt *= ran;
            balanceInt = hight - balanceInt;
            h = hight - balanceInt;
        }else{
            h = ran * 4;
        }
    }

    QPixmap pix(w,h);
    pix.fill(Qt::white);
    w = w / ran;
    h = h / ran;
    // допустимое минимальное значение 4 пикселя
    if (w < 4 || h < 4) return -1;

    paint.begin(&pix);

    for (int i = 0; i < range; i++) {
        for (int j = 0; j < range; j++) {
            if (qrMatrixCopy[i][j] == 1) paint.fillRect(i*w,j*h,w,h,Qt::black);
            if (qrMatrixCopy[i][j] == 0) paint.fillRect(i*w,j*h,w,h,Qt::white);
        }
    }
    paint.end();

    QString fileName = QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()) + ".png";
    fileName = this->workDir + fileName;

    pix.save(fileName, "PNG");

    // освобождение памяти
    for (int i = 0; i < range; i++) {
        delete[] qrMatrix[i];
        delete[] qrMatrixCopy[i];
    }
    delete qrMatrix, qrMatrixCopy;

    return 0;
}

QStringList QRgen::getInfo(){
    QStringList a;
    a.append(tr("Plugin name:"));
    a.append(ME_NAME);
    a.append(tr("Plugin version:"));
    a.append(QString(ME_VERSION)+"-"+QString(GIT_HASH));
    a.append(tr("Build timestamp"));
    a.append(QString(__DATE__)+" "+QString(__TIME__));
    return a;
}

QString QRgen::exec(const QString &data){
    /* прилетело, надо вытащить отсюда:
     * message              - data to draw
     * width                - image width
     * hight                - image hight
     */
    emit this->sigLog(ME_NAME,RPTL::logInfo,tr("Execute"));
    emit this->sigLog(ME_NAME,RPTL::logDebug,tr("Parsing input data"));

    int width = 0;
    int hight = 0;
    // разламываем строку на элементы
    QStringList opts = data.mid(1).split(data.left(1));
    for(int i=0; i<opts.size(); ++i){

        if(QString(opts.at(i)).split("=").at(0) == "width"){
        QString num_x = opts.at(i);
        width=QString(num_x.mid(num_x.indexOf("=")+1)).toInt();
        }

        if(QString(opts.at(i)).split("=").at(0) == "hight"){
        QString num_y = opts.at(i);
        hight=QString(num_y.mid(num_y.indexOf("=")+1)).toInt();
        }

        if(QString(opts.at(i)).split("=").at(0) == "message"){
        this->message = opts.at(i);
        this->message = this->message.mid(this->message.indexOf("=")+1);
        }
    }
    qDebug()<<"width="<<width<<"hight="<<hight<<"message= "<<this->message;

    QString a=QString("width=%1 hight=%2 message=%3").arg(QString::number(width)).arg(QString::number(hight)).arg(this->message);

    // Проверка размера картинки
    if (width <= 0){
        return "N#";
    }
    if (hight <= 0){
        return QString ("N#%1").arg(tr("value not valid"));
    }

    // Step 1: Определяем тип данных
    QString modeIndicator = "";                                                                 // Индикатор режима
    bool isNumeric = true;          // 0
    bool isAlphanumeric = true;     // 1
    //bool isBinaryMode = true;       // 2

    while (true) {

        // TODO: echo "A145" | grep -P '^\d*$'
        // isDigit()
        for (int i=0; i < this->message.size(); ++i) {
            if ( !(this->message.at(i) >= QChar('0') && this->message.at(i) <= QChar('9')) ) {

                isNumeric = false;
                break;
            }
        }
        if (isNumeric) {
            modeIndicator = "0001";         // Numeric Mode
            this->typeData = 0;
            break;
        }

        for (int i = 0; i < this->message.size(); ++i) {

            int tmpNum = this->GetAlphanumeric(this->message.at(i));
            if ( tmpNum < 0 ) {
                isAlphanumeric = false;
                break;
            }
        }
        if (isAlphanumeric) {
            modeIndicator = "0010";         // Alphanumeric Mode
            this->typeData = 1;
            break;
        }

        modeIndicator = "0100";             // Binary Mode
        this->typeData = 2;
        break;

        /*if (!isNumeric && !isAlphanumeric) {
            return QString ("N#%1").arg(tr("Sorry, this mode is not exist."));          // извините, данного режима нет
            break;
        }*/
    }
    qDebug()<<"typeData= "<<this->typeData<<"   modeIndicator= "<<modeIndicator;


    // Step 2: Определение длины сообщения. Выбор версии и определение количества требуемых бит
    this->lengthMessage = this->message.size();
    qDebug()<<"lengthMessage= "<<this->lengthMessage;

    QString binaryLengthMessage = QString::number(this->lengthMessage, 2);
    qDebug()<<"binaryLengthMessage= "<<binaryLengthMessage;
    qDebug()<<"LengthBits= "<<binaryLengthMessage.size();

    // определение версии QR кода
    this->version = this->GetVersion();                                                         // получаем версию QR кода
    if (this->version == 0) return QString ("N#%1").arg(tr("Sorry, version is not found"));
    qDebug()<<"version= "<<this->version;

    // Определяем количество бит
    int amountBits = this->GetNumberBits();
    if (amountBits == 0) return QString ("N#%1").arg(tr("Sorry, unspecified number of bits"));  // неопределенное количество бит
    qDebug()<<"number of bits= "<<amountBits;

    // Добавляем недостоющие нули
    int missingBits = amountBits - binaryLengthMessage.size();
    this->binaryMessage += modeIndicator;
    if ( missingBits != 0 ) {
        this->binaryMessage += QString(missingBits, '0');
    }
    this->binaryMessage += binaryLengthMessage;
    qDebug()<<"binaryMessage="<<this->binaryMessage;

    // готовые переменные для использования: modeIndicator, binaryMessage, message
    // Step 3: Кодирование данных
    int isGood = 0;
    switch (this->typeData) {
    case 0:
        isGood = this->EncodeNumeric();
        break;
    case 1:
        isGood = this->EncodeAlphanumeric();
        break;
    case 2:
        isGood = this->EncodeBinaryMode();
        break;
    default:
        break;
    }
    if (isGood != 0) return QString ("N#%1").arg(tr("Sorry, encoding error messages")); // ошибка кодирования сообщения

    // корректировка размера по таблице
    isGood = this->LengthCorrection();
    if (isGood != 0) return QString ("N#%1").arg(tr("Sorry, encoding error messages")); // ошибка кодирования сообщения
    qDebug()<<"length binaryMessage= "<<this->binaryMessage.size();

    // Step 5: Генерация кода Рида-Соломона
    isGood = this->ReedSolomon();
    if (isGood != 0) return QString ("N#%1").arg(tr("Error generating Reed-Solomon code")); // ошибка генерации кода Рида-Соломона

    // Step 6: Генерация изображения
    isGood = this->ImageGeneration(width, hight);
    if (isGood != 0) return QString ("N#%1").arg(tr("Error, image is formed")); // изображение не сформировано

    return "Y#"+a;
}

bool QRgen::init(const QString &wrkdir){

    this->workDir = wrkdir + "/";
    return true;
}

